package com.training.bt.resource.service;

import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import com.training.bt.resource.dto.ResourceDetailsDTO;
import com.training.bt.resource.dto.ResourceDownloadDTO;
import com.training.bt.resource.dto.ResourceListItemDTO;
import com.training.bt.resource.dto.ResourceMyDetailsDTO;
import com.training.bt.resource.dto.ResourceReviewDTO;
import com.training.bt.resource.dto.ResourceUploadDTO;
import com.training.bt.security.util.SecurityUtils;
import com.training.bt.service.DownloadHistoryService;
import com.training.bt.service.UserPointService;
import com.training.bt.tracker.CustomTrackedTorrent;
import com.training.bt.tracker.CustomTracker;
import com.training.bt.util.TorrentUtils;

import lombok.RequiredArgsConstructor;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dampcake.bencode.Bencode;
import com.dampcake.bencode.Type;
import com.training.bt.common.PageResponseDTO;
import com.training.bt.entity.*;
import com.training.bt.enums.ReviewStatus;
import com.training.bt.enums.Role;
import com.training.bt.mapper.DownloadHistoryMapper;
import com.training.bt.mapper.ResourceMapper;
import com.training.bt.mapper.ResourceTagMapper;
import com.training.bt.mapper.UserMapper;

@Service
@RequiredArgsConstructor
public class ResourceServiceImpl implements ResourceService {
    private final ResourceMapper resourceMapper;
    private final UserMapper userMapper;
    private final CustomTracker customTracker;
    private final UserPointService userPointService;
    private final ResourceTagMapper resourceTagMapper;
    private final DownloadHistoryService downloadHistoryService;
    private final DownloadHistoryMapper downloadHistoryMapper;

    // 定义封面图片保存路径
    private static final String COVER_UPLOAD_DIR = "uploads/cover/";
    // 定义种子文件保存路径
    private static final String TORRENT_UPLOAD_DIR = "uploads/torrents/";

    @Override
    @Transactional
    public Long uploadResource(ResourceUploadDTO dto) throws IOException, NoSuchAlgorithmException {
        MultipartFile multipartFile = dto.getFile();

        // 1. 参数校验（保持原样）
        if (multipartFile == null || multipartFile.isEmpty()) {
            throw new IllegalArgumentException("上传文件不能为空");
        }
        String originalFilename = multipartFile.getOriginalFilename();
        if (originalFilename == null || !originalFilename.toLowerCase().endsWith(".torrent")) {
            throw new IllegalArgumentException("仅支持 .torrent 文件");
        }

        // 2. 读取原始文件字节
        byte[] originalBytes = multipartFile.getBytes();

        Bencode bencode = new Bencode(StandardCharsets.ISO_8859_1);
        Map<String, Object> torrentMap = bencode.decode(originalBytes, Type.DICTIONARY);

        // 获取info字典
        Object infoObj = torrentMap.get("info");
        if (!(infoObj instanceof Map<?, ?>)) {
            throw new IllegalArgumentException("info 字段必须为字典类型，上传的文件不合规");
        }
        Map<String, Object> info = (Map<String, Object>) infoObj;

        // 计算总大小
        long totalSize = 0;
        if (info.containsKey("length")) {
            totalSize = ((Number) info.get("length")).longValue();
        } else if (info.containsKey("files")) {
            Object filesObj = info.get("files");
            if (!(filesObj instanceof List<?>)) {
                throw new IllegalArgumentException("files 字段必须为列表类型");
            }
            List<?> files = (List<?>) filesObj;
            for (Object fileObj : files) {
                if (!(fileObj instanceof Map<?, ?>)) {
                    throw new IllegalArgumentException("文件条目必须为字典类型");
                }
                Map<String, Object> file = (Map<String, Object>) fileObj;
                Number length = (Number) file.get("length");
                if (length == null) {
                    throw new IllegalArgumentException("无效的.torrent文件：文件缺少length字段");
                }
                totalSize += length.longValue();
            }
        } else {
            throw new IllegalArgumentException("无法确定资源大小");
        }

        // 3. 修改announce URL
        User user = SecurityUtils.getCurrentUser();
        String passkey = user.getPasskey();
        byte[] modifiedBytes = TorrentUtils.modifyAnnounce(originalBytes, passkey);

        // 4. 准备保存路径
        String projectRootPath = System.getProperty("user.dir");

        // ========== 保存种子文件到 uploads/torrents/ ==========

        // 清理文件名并添加时间戳
        Path torrentUploadPath = Paths.get(projectRootPath, TORRENT_UPLOAD_DIR);
        Files.createDirectories(torrentUploadPath);
        String storedFilename = "torrent_" + UUID.randomUUID().toString().substring(0, 8)
                + "_" + System.currentTimeMillis() + ".torrent";
        Path torrentTargetFile = torrentUploadPath.resolve(storedFilename);

        // 保存修改后的种子文件
        Files.write(torrentTargetFile, modifiedBytes);

        // ========== 保存封面图片到 uploads/cover/ ==========
        String coverImageUrl = null;
        if (dto.getCoverImage() != null && !dto.getCoverImage().isEmpty()) {
            // 验证图片格式
            String contentType = dto.getCoverImage().getContentType();
            if (!Arrays.asList("image/jpeg", "image/png", "image/gif").contains(contentType)) {
                throw new IllegalArgumentException("仅支持JPEG、PNG、GIF格式的图片");
            }

            // 创建封面存储目录
            Path coverUploadPath = Paths.get(projectRootPath, COVER_UPLOAD_DIR);
            Files.createDirectories(coverUploadPath);

            // 获取文件扩展名
            String originalCoverName = dto.getCoverImage().getOriginalFilename();
            String fileExtension = "";
            if (originalCoverName != null && originalCoverName.contains(".")) {
                fileExtension = originalCoverName.substring(originalCoverName.lastIndexOf("."));
            }

            // 处理无扩展名情况
            if (fileExtension.isEmpty() || !fileExtension.matches("\\.\\w+")) {
                // 根据ContentType设置默认扩展名
                if ("image/jpeg".equals(contentType)) {
                    fileExtension = ".jpg";
                } else if ("image/png".equals(contentType)) {
                    fileExtension = ".png";
                } else if ("image/gif".equals(contentType)) {
                    fileExtension = ".gif";
                } else {
                    fileExtension = ".img";
                }
            }

            // 生成唯一文件名（使用UUID+时间戳）
            String storedCoverName = "cover_" + UUID.randomUUID().toString().substring(0, 8) +
                    "_" + System.currentTimeMillis() + fileExtension;
            Path coverTargetFile = coverUploadPath.resolve(storedCoverName);

            // 保存封面图片
            dto.getCoverImage().transferTo(coverTargetFile.toFile());

            // 生成访问URL（使用URL路径）
            coverImageUrl = "http://192.168.153.1:8080/cover/" + storedCoverName;
        }

        // 5. 构建资源实体
        Resource resource = new Resource();
        resource.setTitle(dto.getTitle());
        resource.setDescription(dto.getDescription());
        resource.setFilePath(torrentTargetFile.toAbsolutePath().toString());
        resource.setCoverImageUrl(coverImageUrl); // 设置封面URL
        resource.setUploadTime(LocalDateTime.now());
        resource.setUploader(user.getId());
        resource.setVisible(false); // 默认未公开
        resource.setSize(totalSize);

        // 设置积分（每100MB一个积分）
        resource.setPoints((int) (totalSize / (100 * 1024 * 1024)));

        // 6. 保存到数据库
        resourceMapper.insert(resource);

        if (dto.getTagIds() != null) {
            dto.getTagIds().forEach(tagId -> {
                ResourceTag rt = new ResourceTag();
                rt.setResourceId(resource.getId());
                rt.setTagId(tagId);
                resourceTagMapper.insert(rt);
            });
        }
        Long generatedId = resource.getId();

        DownloadHistory downloadHistory = new DownloadHistory();
        downloadHistory.setUserId(user.getId());
        downloadHistory.setResourceId(resource.getId());
        downloadHistory.setPointsUsed(0);
        downloadHistory.setDownloadTime(LocalDateTime.now());
        downloadHistoryMapper.insert(downloadHistory);

        // 7. 注册到Tracker
        customTracker.announce(CustomTrackedTorrent.load(torrentTargetFile.toFile()));

        return generatedId;
    }

    @Override
    public PageResponseDTO<ResourceListItemDTO> getList(int page, int size, String keyword, Long tagId) {
        Page<Resource> pageRequest = new Page<>(page, size);

        Page<Resource> result = resourceMapper.selectPagedResourcesByKeywordAndTagId(pageRequest, keyword, tagId);

        List<ResourceListItemDTO> dtoList = result.getRecords().stream().map(item -> {
            Resource resource = resourceMapper.selectById(item.getId());
            ResourceListItemDTO dto = new ResourceListItemDTO();

            dto.setId(resource.getId());
            dto.setTitle(resource.getTitle());
            dto.setDescription(resource.getDescription());
            dto.setUploadTime(resource.getUploadTime());
            dto.setCoverImageUrl(resource.getCoverImageUrl());

            System.out.println(resource.getUploader());
            User user = userMapper.selectById(resource.getUploader());
            if (user != null) {
                dto.setUploaderName(user.getUsername());
            } else {
                dto.setUploaderName("未知用户");
            }
            return dto;

        }).toList();

        return new PageResponseDTO<>(result.getTotal(), dtoList);

    }

    @Override
    public ResponseEntity<ResourceDetailsDTO> findById(Long id) {
        Resource resource = resourceMapper.selectById(id);

        if (resource == null || !Boolean.TRUE.equals(resource.getVisible())) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "资源不存在或未公开");
        }

        ResourceDetailsDTO dto = new ResourceDetailsDTO();
        System.out.println(resource.getId() + resource.getTitle());
        dto.setId(resource.getId());
        dto.setTitle(resource.getTitle());
        dto.setDescription(resource.getDescription());
        dto.setUploadTime(resource.getUploadTime());
        dto.setCoverImageUrl(resource.getCoverImageUrl());
        dto.setSize(resource.getSize());
        dto.setPoints(resource.getPoints());
        dto.setDownloadCount(resource.getDownloadCount());
        if ((downloadHistoryService.exist(SecurityUtils.getCurrentUser().getId(), resource.getId())) >= 1) {
            dto.setIsPay(true);
        } else {
            dto.setIsPay(false);
        }

        User user = userMapper.selectById(resource.getUploader());
        if (user != null) {
            dto.setUploaderName(user.getUsername());
        } else {
            dto.setUploaderName("未知用户");
        }

        return ResponseEntity.ok(dto);
    }

    @Override
    public PageResponseDTO<ResourceMyDetailsDTO> getMyList(int page, int size) {
        User user = SecurityUtils.getCurrentUser();

        Page<Resource> pageRequest = new Page<>(page, size);

        LambdaQueryWrapper<Resource> wrapper = new LambdaQueryWrapper<Resource>()
                .eq(Resource::getUploader, user.getId());

        Page<Resource> result = resourceMapper.selectPage(pageRequest, wrapper);
        List<ResourceMyDetailsDTO> dtoList = result.getRecords().stream().map(resource -> {
            ResourceMyDetailsDTO dto = new ResourceMyDetailsDTO();
            dto.setId(resource.getId());
            dto.setTitle(resource.getTitle());
            dto.setDescription(resource.getDescription());
            dto.setUploadTime(resource.getUploadTime());
            dto.setUploaderName(user.getUsername());
            dto.setCoverImageUrl(resource.getCoverImageUrl());
            dto.setReviewStatus(resource.getReviewStatus());

            return dto;
        }).toList();

        return new PageResponseDTO<>(result.getTotal(), dtoList);
    }

    @Override
    public void deleteById(Long id) {
        Resource resource = resourceMapper.selectById(id);

        User user = SecurityUtils.getCurrentUser();

        // if (resource.getUploader() != user.getId() ||
        // user.getRole().equals(Role.ADMIN)) {
        // throw new ResponseStatusException(HttpStatus.UNAUTHORIZED,
        // "非本人进行删除操作或非管理进行操作");
        // }
        if (!resource.getUploader().equals(user.getId()) && !user.getRole().equals(Role.ADMIN)) {
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "没有权限删除此资源");
        }

        resourceMapper.deleteById(id);
    }

    @Override
    public ResourceDownloadDTO getResourceFilePath(Long id) {
        Resource resource = resourceMapper.selectById(id);

        if (resource == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "资源不存在");
        }

        if (!Boolean.TRUE.equals(resource.getVisible())) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "资源未公开，无法下载");
        }

        String fullPath = resource.getFilePath();
        String fillname = Paths.get(fullPath).getFileName().toString();

        return new ResourceDownloadDTO(fullPath, fillname);
    }

    @Override
    public void reviewResource(ResourceReviewDTO dto) {
        Resource resource = resourceMapper.selectById(dto.getId());

        if (resource == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "资源不存在");
        }

        if (dto.getApprove()) {
            resource.setVisible(true);
            resource.setReviewStatus(ReviewStatus.PASS);

            userPointService.addPoints(resource.getUploader(), resource.getPoints(), resource.getSize());
        } else {
            resource.setReviewStatus(ReviewStatus.REFUSE);
            resource.setRejectReason(dto.getReason());
        }

        resourceMapper.updateById(resource);

        // 后续有通知给上传者的步骤
    }

    @Override
    public PageResponseDTO<ResourceListItemDTO> getPendingResource(int page, int size) {
        Page<Resource> requestPage = new Page<>(page, size);

        LambdaQueryWrapper<Resource> wrapper = new LambdaQueryWrapper<Resource>()
                .eq(Resource::getReviewStatus, ReviewStatus.WAIT);

        Page<Resource> result = resourceMapper.selectPage(requestPage, wrapper);

        List<ResourceListItemDTO> dtoList = result.getRecords().stream().map(resource -> {
            ResourceListItemDTO dto = new ResourceListItemDTO();
            dto.setId(resource.getId());
            dto.setTitle(resource.getTitle());
            dto.setDescription(resource.getDescription());
            dto.setUploadTime(resource.getUploadTime());
            dto.setUploaderName(userMapper.selectById(resource.getUploader()).getUsername());
            dto.setCoverImageUrl(resource.getCoverImageUrl());

            return dto;
        }).toList();

        return new PageResponseDTO<>(result.getTotal(), dtoList);
    }

    @Override
    public ResourceListItemDTO getReviewResource(Long id) {
        Resource resource = resourceMapper.selectById(id);

        ResourceListItemDTO dto = new ResourceListItemDTO();
        dto.setId(resource.getId());
        dto.setTitle(resource.getTitle());
        dto.setDescription(resource.getDescription());
        dto.setUploadTime(resource.getUploadTime());
        dto.setUploaderName(userMapper.selectById(resource.getUploader()).getUsername());
        dto.setCoverImageUrl(resource.getCoverImageUrl());

        return dto;
    }

    @Override
    public Resource selectById(Long id) {
        return resourceMapper.selectById(id);
    }

    @Override
    public void updateDownloadCount(Resource resource) {
        resourceMapper.updateById(resource);
    }

    @Override
    public List<Resource> getAllActiveResources() {
        LambdaQueryWrapper<Resource> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Resource::getReviewStatus, ReviewStatus.PASS) // 审核通过的
                .eq(Resource::getVisible, true); // 可见的
        return resourceMapper.selectList(wrapper);
    }
}
